/******************************************************************************/
/*!

\file   Vector.hpp
\author M.J. Gardner
\date   12/3/2011
\brief
    A templated vector math library for easy n-dimensional vectors.
    Does all the things n-dimensional vectors do, including the 3-dimensional
    version of cross product. 7-dimensional cross product isn't here, because
    when would you even want that?

    Will be superseded by matrices when nXm matrix library is complete

*/
/******************************************************************************/
#ifndef VECTOR_TEMPLATE_HPP
#define VECTOR_TEMPLATE_HPP

#include "MathUtil.hpp"     //epsilon
#include <ostream>          //output
#include <initializer_list>
#include <array>

namespace Vec
{

  //////////////////////////////////////////
  /*
  TODO:
  //tensor product  //requires matrices, so just do it for those

  specific constructor (i.e. Vec3(1, 2, 3);)


  !!!!
  TEST ALL THE THINGS!
  !!!!
  */
  //////////////////////////////////////////


  //nonmember nonfriend functions for Vector and Vector::Iterator are declared
  //below the end of the Vector struct
  template<unsigned int N = 3, typename T = float>
  struct Vector
  {

    struct Iterator //random access iterator type
    {
      Iterator(const typename std::array<T, N>::Iterator &it);
      Iterator(const Vector::Iterator &it);
      ~Iterator();

      //all other comparison operators exist as non-members
      bool operator==(const typename Vector<N, T>::Iterator &rhs);
      bool operator<(const typename Vector<N, T>::Iterator &rhs);
      //can pre and post increment and decrement
      Vector<N, T>::Iterator &operator++(void);
      Vector<N, T>::Iterator operator++(int);
      Vector<N, T>::Iterator &operator--(void);
      Vector<N, T>::Iterator operator--(int);
      //assignment and compound assignment
      Vector<N, T>::Iterator &operator=(const Vector<N, T>::Iterator &rhs);
      Vector<N, T>::Iterator &operator+=(const int rhs);
      Vector<N, T>::Iterator &operator-=(const int rhs);
      //iterator-from-iterator subtraction (other addition/subtraction operators
      //are non-member)
      int operator-(const typename Vector<N, T>::Iterator &rhs);
      //dereference
      T &operator*(void);
      //offset dereference
      T &operator[](const int offset);

    private:
      typename std::array<T, N>::iterator _elem_it;
    };

    Vector(); //constructs with all 0s
    Vector(std::initializer_list<T> list); //initializer lists aww yeah
    Vector(T *rhs); //construct from array
    Vector(const typename std::array<T, N>::iterator &begin,
            const typename std::array<T, N>::iterator &end);
    Vector(std::array<T, N> &rhs);
    Vector(const Vector &rhs);
    ~Vector();


  /*
    Standard vector and scalar mathematical operations.
    They do just what you'd expect.
  */
    Vector<N, T> &operator=(const Vector &rhs);
    Vector<N, T> &operator*=(const T rhs);
    Vector<N, T> &operator/=(const T rhs);
    Vector<N, T> &operator+=(const Vector<N, T> &rhs);
    Vector<N, T> &operator-=(const Vector<N, T> &rhs);
    Vector<N, T> operator-(void) const;  //unary negation


    //output a vector's elements
    //friend std::ostream &operator<< <> (std::ostream &out, const Vector<N, T> &vec);

    //normalize in place
    void Normalize(void);

    //access vector element
    const T &operator[](const unsigned int index) const;
    T &operator[](const unsigned int index);


    //iterator access
    typename Vector<N, T>::Iterator Begin(void);
    typename Vector<N, T>::Iterator End(void);


  private:
    //elements of the vector, of course
    std::array<T, N> _elems;


    //helper for assignment and copy
    void CopyElems(const Vector &rhs);
  };

  //aliases for common vectors
  typedef Vector<2, float> Vec2;
  typedef Vector<3, float> Vec3;
  typedef Vector<4, float> Vec4;


  //TODO: when GCC 4.7 releases, update to it and switch to the alias template
  //commented out below
#define VEC_IT typename Vector<N, T>::Iterator
  // //alias templates are part of the C++11 specification, but they're not
  // //in GCC yet
  // template<unsigned int N, typename T>
  // using Vec_It = Vector<N, T>::Iterator;

////////////////////////////////////////
  /*
  nonmember iterator functions
  */
  // full set of comparisons (== and < are members)
  template<unsigned int N, typename T>
  bool operator!=(const VEC_IT &lhs, const VEC_IT &rhs);
  template<unsigned int N, typename T>
  bool operator>(const VEC_IT &lhs, const VEC_IT &rhs);
  template<unsigned int N, typename T>
  bool operator<=(const VEC_IT &lhs, const VEC_IT &rhs);
  template<unsigned int N, typename T>
  bool operator>=(const VEC_IT &lhs, const VEC_IT &rhs);
  //add ints, subtract ints or iterators (iterator-from-iterator subtraction is a member)
  template<unsigned int N, typename T>
  VEC_IT operator+(const VEC_IT &lhs, const int rhs);
  template<unsigned int N, typename T>
  VEC_IT operator+(const int lhs, const VEC_IT &rhs);
  template<unsigned int N, typename T>
  VEC_IT operator-(const VEC_IT &lhs, const int rhs);



////////////////////////////////////////
  /*
  Vector operations that don't need to be members or friends.
  they do the obvious things you'd expect, unless you somehow don't know how
  vectors work at all.
  */
  template<unsigned int N, typename T>
  bool  operator==(const Vector<N, T> &lhs, const Vector<N, T> &rhs);
  template<unsigned int N, typename T>
  bool  operator!=(const Vector<N, T> &lhs, const Vector<N, T> &rhs);
  template<unsigned int N, typename T>
  Vector<N, T> operator+(const Vector<N, T> &lhs, const Vector<N, T> &rhs);
  template<unsigned int N, typename T>
  Vector<N, T> operator-(const Vector<N, T> &lhs, const Vector<N, T> &rhs);
  template<unsigned int N, typename T>
  Vector<N, T> operator*(const Vector<N, T> &lhs, const T rhs);
  template<unsigned int N, typename T>
  Vector<N, T> operator*(const T lhs, const Vector<N, T> &rhs);
  template<unsigned int N, typename T>
  Vector<N, T> operator/(const Vector<N, T> &lhs, const T rhs);

  //operator* with Vectors for both args and InnerProduct both do a scalar inner
  //product operation (aka dot product)
  template<unsigned int N, typename T>
  T operator*(const Vector<N, T> &lhs, const Vector<N, T> &rhs);
  template<unsigned int N, typename T>
  T InnerProduct(const Vector<N, T> &lhs, const Vector<N, T> &rhs);
  //finds the distance between 2 vectors (a scalar)
  template<unsigned int N, typename T>
  T Distance(const Vector<N, T> &lhs, const Vector<N, T> &rhs);
  //find the square of the distance between 2 vectors. Use this version if simply comparing
  //2 distances, since it is more efficient due to not needing to do a square root.
  template<unsigned int N, typename T>
  T DistanceSquared(const Vector<N, T> &lhs, const Vector<N, T> &rhs);
  //calculate the magnitude, i.e. the length of the vector
  template<unsigned int N, typename T>
  T Magnitude(const Vector<N, T> &vec);
  //Magnitude without the square root.
  //Use if you just need to compare 2 magnitudes or something, since it's faster
  template<unsigned int N, typename T>
  T MagnitudeSquared(const Vector<N, T> &vec);
  //get a vector with the same direction as the argument, but magnitude 1
  template<unsigned int N, typename T>
  Vector<N, T> Normalized(const Vector<N, T> &vec);
  //the dimension of the vector(equal to the template argument N)
  template<unsigned int N, typename T>
  unsigned int Dimension(const Vector<N, T> &vec);

  //cross product only exists in 3 and 7 dimensions, and I don't care enough about
  //7-dimensional vectors to implement it for them.
  template<typename T>
  Vector<3, T> CrossProduct(const Vector<3, T> &lhs, const Vector<3, T> &rhs);
  //output all of the elements of a vector
  template<unsigned int N, typename T>
  std::ostream &operator<<(std::ostream &out, const Vector<N, T> &vec);




}

#include "Vector.inl"

#endif
